package MLDA_Process;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map.Entry;

import org.apache.commons.math3.special.Beta;
import org.apache.commons.math3.special.Gamma;

import Commons.IOUtil;

public class DsparseTM implements Runnable {

    class ModelParams {
        // hyper parameters
        public double s;
        public double r;

        //
        public double x;
        public double y;

        //
        public double pi;
        public double pi_;

        //
        public double omega;
        public double omega_;

        public int T;

        public List<double[][]> gamma;

        public double[][] alpha;

        public double[][] beta;
    }

    class Counts {
        public double[][] m_njk; // D * T
        public double[] m_nj;    // 1 * D
        public double[][] m_nkw; // T * W
        public double[] m_nk;    // 1 * T

        public double[] m_aj; // 1 * D
        public double[] m_bk; // 1 * T
    }

    class Dataset {
        public int D;
        public int W;
        public int[] Md;
        public List<int[]> docs_w;
        public List<int[]> docs_c;
    }

    class GibbsCounts {
        public int[][] nw;
        public int[][] nd;
        public int[] nwsum;
        public int[] ndsum;

        public List<HashSet<Integer>> aj;
        public List<HashSet<Integer>> bk;
    }

    class GibbsDataset {
        public int D;
        public int W;
        public List<int[]> docs;
        public HashMap<String, Integer> w2i;
        public HashMap<Integer, String> i2w;
    }

    public int maxiter = 200;
    public int saveStep = 1000;
    public double convtol = -1;

    public GibbsDataset g_ds;
    public GibbsCounts g_c;
    public Dataset ds;
    public Counts c;
    public ModelParams mp;
    public String rootPath;
    public String filePath;
    public String ParentFile;
    public int ntopics;

    public DsparseTM(String rootpath, String filePath, int ntopics, int niters, int saveStep) {
        g_ds = new GibbsDataset();
        g_c = new GibbsCounts();
        ds = new Dataset();
        c = new Counts();
        mp = new ModelParams();
        this.rootPath = rootpath;
        this.filePath = filePath;
        this.maxiter = niters;
        this.saveStep = saveStep;
        Path sfile = java.nio.file.Paths.get(filePath);
        this.ParentFile = java.nio.file.Paths.get(sfile.getParent().toString(), "DsparseTM_" + niters + "/" + this.rootPath).toString() + "/";

        File tfg = new File(this.ParentFile);
        if (!tfg.exists() || !tfg.isDirectory())
            tfg.mkdirs();

        this.ntopics = ntopics;
    }

    public void setModelParams() {
        mp.T = 20;

        mp.x = 1.0;
        mp.y = 1.0;
        mp.s = 1.0;
        mp.r = 1.0;
        mp.pi = 0.1;
        mp.pi_ = 1e-7;
        mp.omega = 0.01;
        mp.omega_ = 1e-7;
    }

    // load GibbsDataset by reading plant text file
    public void loadGibbsDataset(String filePath) {
        g_ds.D = 0;
        g_ds.W = 0;
        g_ds.docs = new ArrayList<int[]>();
        g_ds.w2i = new HashMap<String, Integer>();
        g_ds.i2w = new HashMap<Integer, String>();

        BufferedReader reader = IOUtil.getReader(filePath, "utf-8");
        String lineDoc = null;

        try {
            lineDoc = reader.readLine();
            while (lineDoc != null) {
                String[] words = lineDoc.trim().split(" ");
                int[] word_ids = new int[words.length];
                for (int i = 0; i != words.length; i++) {
                    if (!g_ds.w2i.containsKey(words[i])) {
                        g_ds.w2i.put(words[i], g_ds.w2i.size());
                        g_ds.i2w.put(g_ds.w2i.get(words[i]), words[i]);
                    }
                    word_ids[i] = g_ds.w2i.get(words[i]);
                }
                g_ds.docs.add(word_ids);
                lineDoc = reader.readLine();
            }
            g_ds.D = g_ds.docs.size();
            g_ds.W = g_ds.w2i.size();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void convert_dataset() {
        ds.D = g_ds.D;
        ds.W = g_ds.W;
        ds.Md = new int[ds.D];
        ds.docs_w = new ArrayList<int[]>();
        ds.docs_c = new ArrayList<int[]>();

        for (int j = 0; j != g_ds.docs.size(); j++) {
            HashMap<Integer, Integer> i2c = new HashMap<Integer, Integer>();
            for (int i = 0; i != g_ds.docs.get(j).length; i++) {
                int w = g_ds.docs.get(j)[i];
                if (!i2c.containsKey(w))
                    i2c.put(w, 1);
                else
                    i2c.put(w, i2c.get(w) + 1);
            }
            ds.Md[j] = i2c.size();
            int[] docs_w = new int[i2c.size()];
            int[] docs_c = new int[i2c.size()];
            int k = 0;
            for (int wid : i2c.keySet()) {
                docs_w[k] = wid;
                docs_c[k] = i2c.get(wid);
                k++;
            }
            ds.docs_w.add(docs_w);
            ds.docs_c.add(docs_c);
        }
    }

    public void initGibbsCounts() {
        int T = mp.T;
        int D = g_ds.D;
        int W = g_ds.W;

        g_c.nd = new int[D][T];
        g_c.nw = new int[W][T];
        g_c.nwsum = new int[T];
        g_c.ndsum = new int[D];

        g_c.aj = new ArrayList<HashSet<Integer>>();
        g_c.bk = new ArrayList<HashSet<Integer>>();

        for (int k = 0; k != T; k++) {
            g_c.bk.add(new HashSet<Integer>());
            g_c.nwsum[k] = 0;
            for (int j = 0; j != D; j++) {
                if (k == 0) {
                    g_c.aj.add(new HashSet<Integer>());
                    g_c.ndsum[j] = 0;
                }
                g_c.nd[j][k] = 0;
            }
            for (int w = 0; w != W; w++) {
                g_c.nw[w][k] = 0;
            }
        }


    }

    public void gibbs_online_init() {
        this.initGibbsCounts();

        int T = mp.T;
        int D = g_ds.D;
        int W = g_ds.W;

        double x = mp.x;
        double y = mp.y;
        double s = mp.s;
        double r = mp.r;
        double pi = mp.pi;
        double pi_ = mp.pi_;
        double omega = mp.omega;
        double omega_ = mp.omega_;

        double[] p = new double[T];
        for (int j = 0; j != D; j++) {
            for (int i = 0; i != g_ds.docs.get(j).length; i++) {
                int w = g_ds.docs.get(j)[i];

                for (int k = 0; k != T; k++) {
                    p[k] = (g_c.nw[w][k] + omega) / (g_c.nwsum[k] + W * omega) *
                            (g_c.nd[j][k] + pi);
                }
                for (int k = 1; k != T; k++) {
                    p[k] += p[k - 1];
                }
                double u = Math.random() * p[T - 1];
                for (int k = 0; k != T; k++) {
                    if (p[k] > u) {
                        g_c.nw[w][k]++;
                        g_c.nd[j][k]++;
                        g_c.nwsum[k]++;
                        g_c.ndsum[j]++;

                        g_c.aj.get(j).add(k);
                        g_c.bk.get(k).add(w);
                        break;
                    }
                }
            }
        }

        mp.gamma = new ArrayList<double[][]>();
        for (int j = 0; j != D; j++) {
            int Md = ds.Md[j];
            double[][] gamma = new double[T][Md];
            for (int i = 0; i != Md; i++) {
                int w = ds.docs_w.get(j)[i];
                double normsum = 0;
                for (int k = 0; k != T; k++) {
                    gamma[k][i] = (g_c.nw[w][k] + omega)
                            / (g_c.nwsum[k] + W * omega)
                            * (g_c.nd[j][k] + pi);
                    normsum += gamma[k][i];
                }
                // Normalize
                for (int k = 0; k != T; k++) {
                    gamma[k][i] /= normsum;
                }
            }
            mp.gamma.add(gamma);
        }

        c.m_njk = new double[D][T];
        c.m_nkw = new double[T][W];
        c.m_nk = new double[T];
        c.m_nj = new double[D];
        for (int d = 0; d != D; d++) {
            for (int t = 0; t != T; t++) {
                if (t == 0) {
                    c.m_nj[d] = 0.0;
                }
                c.m_njk[d][t] = 0.0;
            }
        }

        for (int t = 0; t != T; t++) {
            for (int w = 0; w != W; w++) {
                if (w == 0) {
                    c.m_nk[t] = 0.0;
                }
                c.m_nkw[t][w] = 0.0;
            }
        }

        for (int j = 0; j != D; j++) {
            for (int i = 0; i != ds.Md[j]; i++) {
                int w = ds.docs_w.get(j)[i];
                int ct = ds.docs_c.get(j)[i];
                for (int k = 0; k != T; k++) {
                    double mc = ct * mp.gamma.get(j)[k][i];
                    c.m_njk[j][k] += mc;
                    c.m_nkw[k][w] += mc;
                    c.m_nk[k] += mc;
                    c.m_nj[j] += mc;
                }
            }
        }

        mp.alpha = new double[D][T];
        double logGamma_pi_plus_pi_ = Gamma.logGamma(pi + pi_);
        for (int j = 0; j != D; j++) {
            double aj = g_c.aj.get(j).size();
            double normsum = 0.0;
            for (int k = 0; k != T; k++) {
                double a_jk_1 = 0.0;
                a_jk_1 += Math.log(r + aj);
                a_jk_1 += Gamma.logGamma(c.m_njk[j][k] + pi + pi_);
                a_jk_1 += Beta.logBeta(pi + T * pi_ + pi * aj
                        , c.m_nj[j] + pi * aj + T * pi_);
                a_jk_1 = Math.exp(a_jk_1);

//				a_jk_1 = (r + aj) * Gamma.gamma(c.m_njk[j][k] + pi + pi_)
//						* Math.exp(Beta.logBeta(pi + T*pi_ + pi*aj, c.m_nj[j] + pi*aj + T*pi_));

                double a_jk_0 = 0.0;
                a_jk_0 += Math.log(s + T - 1 - aj) + logGamma_pi_plus_pi_;
                a_jk_0 += Beta.logBeta(T * pi_ + pi * aj
                        , c.m_nj[j] + pi + pi * aj + T * pi_);
                a_jk_0 = Math.exp(a_jk_0);

//				a_jk_0 = (s + T - 1 - aj) * Gamma.gamma(pi + pi_)
//						* Math.exp(Beta.logBeta(T*pi_ + pi*aj, c.m_nj[j] + pi + pi*aj + T*pi_));

                if (Double.isInfinite(a_jk_1)) {
                    System.out.println("inf a 1");
                    mp.alpha[j][k] = 1.0;
                } else {
                    mp.alpha[j][k] = a_jk_1 / (a_jk_1 + a_jk_0);
                }
                normsum += mp.alpha[j][k];
            }
//			for (int k = 0; k != T; k++) {
//				mp.alpha[j][k] /= normsum;
//			}
        }

        c.m_aj = new double[D];
        for (int j = 0; j != D; j++) {
            c.m_aj[j] = 0;
            for (int k = 0; k != T; k++) {
                c.m_aj[j] += mp.alpha[j][k];
            }
        }

        mp.beta = new double[T][W];
        double logGamma_omega_plus_omega_ = Gamma.logGamma(omega + omega_);
        for (int k = 0; k != T; k++) {
            double bk = g_c.bk.get(k).size();
            double normsum = 0.0;
            for (int w = 0; w != W; w++) {
                double b_kr_1 = 0.0;
                b_kr_1 += Math.log(x + bk) + Gamma.logGamma(c.m_nkw[k][w] + omega + omega_);
                b_kr_1 += Beta.logBeta(omega + W * omega_ + omega * bk
                        , c.m_nk[k] + omega * bk + W * omega_);
                b_kr_1 = Math.exp(b_kr_1);

//				b_kr_1 = (x + bk) * Gamma.gamma(c.m_nkw[k][w] + omega + omega_)
//						*Math.exp(Beta.logBeta(omega + W * omega_ + omega * bk, c.m_nk[k] + omega * bk + W * omega_));

                double b_kr_0 = 0.0;
                b_kr_0 += Math.log(y + W - 1 - bk) + logGamma_omega_plus_omega_;
                b_kr_0 += Beta.logBeta(W * omega_ + omega * bk
                        , c.m_nk[k] + omega + omega * bk + W * omega_);
                b_kr_0 = Math.exp(b_kr_0);

//				b_kr_0 = (y + W - 1 - bk) * Gamma.gamma(omega + omega_)
//						* Math.exp(Beta.logBeta(W * omega_ + omega * bk, c.m_nk[k] + omega + omega * bk + W * omega_));
                if (Double.isInfinite(b_kr_1)) {
//					System.out.println("inf b 1");
                    mp.beta[k][w] = 1.0;
                } else {
                    mp.beta[k][w] = b_kr_1 / (b_kr_1 + b_kr_0);
                }
                normsum += mp.beta[k][w];
            }
//			for (int w = 0; w != W; w++) {
//				mp.beta[k][w] /= normsum;
//			}
        }

        c.m_bk = new double[T];
        for (int k = 0; k != T; k++) {
            c.m_bk[k] = 0;
            for (int w = 0; w != W; w++) {
                c.m_bk[k] += mp.beta[k][w];
            }
        }
    }

    public double dsparsetm_infer() {
        int D = ds.D;
        int T = mp.T;
        int W = ds.W;

        double x = mp.x;
        double y = mp.y;
        double s = mp.s;
        double r = mp.r;
        double pi = mp.pi;
        double pi_ = mp.pi_;
        double omega = mp.omega;
        double omega_ = mp.omega_;

        double normsum;

        double logGamma_pi_plus_pi_ = Gamma.logGamma(pi + pi_);
        for (int j = 0; j != D; j++) {
//			normsum = 0.0;
            for (int k = 0; k != T; k++) {
                c.m_aj[j] -= mp.alpha[j][k];
                double aj = c.m_aj[j];
                double njk = c.m_njk[j][k];
                double nj = c.m_nj[j];
                double a_jk_1 = 0.0;
                a_jk_1 += Math.log(r + aj);
                a_jk_1 += Gamma.logGamma(njk + pi + pi_);
                a_jk_1 += Beta.logBeta(pi + T * pi_ + pi * aj
                        , nj + pi * aj + T * pi_);
                a_jk_1 = Math.exp(a_jk_1);

//				a_jk_1 = (r + aj) * Gamma.gamma(njk + pi + pi_)
//						* Math.exp(Beta.logBeta(pi + T*pi_ + pi*aj, nj + pi*aj + T*pi_));

                double a_jk_0 = 0.0;
                a_jk_0 += Math.log(s + T - 1 - aj) + logGamma_pi_plus_pi_;
                a_jk_0 += Beta.logBeta(T * pi_ + pi * aj
                        , nj + pi + pi * aj + T * pi_);
                a_jk_0 = Math.exp(a_jk_0);

//				a_jk_0 = (s + T - 1 - aj) * Gamma.gamma(pi + pi_)
//						* Math.exp(Beta.logBeta(T*pi_ + pi*aj, nj + pi + pi*aj + T*pi_));

                if (Double.isInfinite(a_jk_1)) {
                    System.out.println("inf a 2");
                    mp.alpha[j][k] = 1.0;
                } else {
                    mp.alpha[j][k] = a_jk_1 / (a_jk_1 + a_jk_0);
                }
//				normsum += mp.alpha[j][k]; 
            }
            for (int k = 0; k != T; k++) {
//				mp.alpha[j][k] /= normsum;
                c.m_aj[j] += mp.alpha[j][k];
            }
        }

        double logGamma_omega_plus_omega_ = Gamma.logGamma(omega + omega_);
        for (int k = 0; k != T; k++) {
//			normsum = 0.0;
            for (int w = 0; w != W; w++) {
                c.m_bk[k] -= mp.beta[k][w];
                double bk = c.m_bk[k];
                double b_kr_1 = 0.0;
                double nkw = c.m_nkw[k][w];
                double nk = c.m_nk[k];

                b_kr_1 += Math.log(x + bk) + Gamma.logGamma(nkw + omega + omega_);
                b_kr_1 += Beta.logBeta(omega + W * omega_ + omega * bk
                        , nk + omega * bk + W * omega_);
                b_kr_1 = Math.exp(b_kr_1);

//				b_kr_1 = (x + bk) * Gamma.gamma(nkw + omega + omega_)
//						*Math.exp(Beta.logBeta(omega + W*omega_ + omega*bk, nk + omega*bk + W*omega_));

                double b_kr_0 = 0.0;
                b_kr_0 += Math.log(y + W - 1 - bk) + logGamma_omega_plus_omega_;
                b_kr_0 += Beta.logBeta(W * omega_ + omega * bk
                        , nk + omega + omega * bk + W * omega_);
                b_kr_0 = Math.exp(b_kr_0);

//				b_kr_0 = (y + W - 1 - bk) * Gamma.gamma(omega + omega_)
//						* Math.exp(Beta.logBeta(W*omega_ + omega*bk, nk + omega + omega*bk + W*omega_));
                if (Double.isInfinite(b_kr_1)) {
//					System.out.println("inf b 2");
                    mp.beta[k][w] = 1.0;
                } else {
                    mp.beta[k][w] = b_kr_1 / (b_kr_1 + b_kr_0);
                }
//				normsum += mp.beta[k][w];
            }
            for (int w = 0; w != W; w++) {
//				mp.beta[k][w] /= normsum;
                c.m_bk[k] += mp.beta[k][w];
            }
        }

        double max_delta = Double.MIN_VALUE; // max absolute change in gamma(convergence)
        double cur_delta;
        double[] old_gamma = new double[T];

        for (int j = 0; j != D; j++) {
            int Md = ds.Md[j];
            int[] doc_w = ds.docs_w.get(j);
            int[] doc_c = ds.docs_c.get(j);
            double[][] gamma = mp.gamma.get(j);

            for (int i = 0; i != Md; i++) {
                int wi = doc_w[i];
                int ci = doc_c[i];
                for (int k = 0; k != T; k++) {
                    double mc = -ci * mp.gamma.get(j)[k][i];
                    c.m_njk[j][k] += mc;
                    c.m_nkw[k][wi] += mc;
                    c.m_nk[k] += mc;
                    c.m_nj[j] += mc;
                }
                normsum = 0;
                for (int k = 0; k != T; k++) {
                    old_gamma[k] = gamma[k][i];
                    double m_njk = c.m_njk[j][k];
                    double m_nkw = c.m_nkw[k][wi];
                    double m_nk = c.m_nk[k];
                    double m_bk = c.m_bk[k];
                    double newgamma = gamma[k][i] = (m_nkw + omega * mp.beta[k][wi] + omega_)
                            / (m_nk + omega * m_bk + W * omega_)
                            * (m_njk + pi * mp.alpha[j][k] + pi_);
                    mp.gamma.get(j)[k][i] = newgamma;
                    normsum += newgamma;
                }
                cur_delta = 0;
                for (int k = 0; k != T; k++) {
                    mp.gamma.get(j)[k][i] /= normsum;
                    cur_delta += Math.abs(mp.gamma.get(j)[k][i] - old_gamma[k]);
                }
                if (cur_delta > max_delta)
                    max_delta = cur_delta;
                for (int k = 0; k != T; k++) {
                    double mc = ci * mp.gamma.get(j)[k][i];
                    c.m_njk[j][k] += mc;
                    c.m_nkw[k][wi] += mc;
                    c.m_nk[k] += mc;
                    c.m_nj[j] += mc;
                }
            }
        }
        return max_delta;
    }

    public double[][] est_phi() {
        int W = g_ds.W;
        int T = mp.T;

        double[][] g_phi = new double[T][W];
        for (int k = 0; k != T; k++) {
            for (int w = 0; w != W; w++) {
                g_phi[k][w] = (c.m_nkw[k][w] + mp.omega * mp.beta[k][w] + mp.omega_)
                        / (c.m_nk[k] + mp.omega * c.m_bk[k] + W * mp.omega_);
            }
        }
        return g_phi;
    }

    public ArrayList<List<Entry<String, Double>>> sorted_topicwords(
            double[][] phi) {
        ArrayList<List<Entry<String, Double>>> res = new ArrayList<List<Entry<String, Double>>>();

        for (int k = 0; k != mp.T; k++) {
            HashMap<String, Double> term2weight = new HashMap<String, Double>();
            for (String term : g_ds.w2i.keySet())
                term2weight.put(term, phi[k][g_ds.w2i.get(term)]);

            List<Entry<String, Double>> pairs = new ArrayList<Entry<String, Double>>(
                    term2weight.entrySet());
            Collections.sort(pairs, new Comparator<Entry<String, Double>>() {
                public int compare(Entry<String, Double> o1,
                                   Entry<String, Double> o2) {
                    return (o2.getValue().compareTo(o1.getValue()));
                }
            });

            res.add(pairs);
        }

        return res;
    }

    public void print_topics(int top_n) {
        double phi[][] = this.est_phi();
        ArrayList<List<Entry<String, Double>>> pairs_list = this
                .sorted_topicwords(phi);
        for (int k = 0; k != mp.T; k++) {
            System.out.println("Topic " + k + ":");
            for (int i = 0; i != top_n; i++) {
                System.out.println(pairs_list.get(k).get(i).getKey() + " "
                        + pairs_list.get(k).get(i).getValue());
            }
        }
    }

    public void save_phi(String outdir, String model_name) {
        double[][] phi = this.est_phi();

        double T = mp.T;
        double W = ds.W;

        BufferedWriter writer = IOUtil.getWriter(outdir + model_name + ".phi", "utf-8");
        try {
            for (int k = 0; k != T; k++) {
                for (int w = 0; w != W; w++) {
                    writer.append(phi[k][w] + " ");
                }
                writer.append("\n");
            }
            writer.flush();
            writer.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public double[][] est_theta() {
        int D = g_ds.D;
        int T = mp.T;

        double[][] g_the = new double[D][T];
        for (int d = 0; d != D; d++) {
            for (int t = 0; t != T; t++) {
                g_the[d][t] = (c.m_njk[d][t] + mp.pi * mp.alpha[d][t] + mp.pi_)
                        / (c.m_nj[d] + mp.pi * c.m_aj[d] + T * mp.pi_);
            }
        }
        return g_the;
    }

    public void save_wordmap(String outdir) {
        BufferedWriter writer = IOUtil.getWriter(outdir + "wordmap.txt", "utf-8");

        try {
            writer.append(g_ds.w2i.size() + "\n");
            for (String term : g_ds.w2i.keySet())
                writer.append(term + " " + g_ds.w2i.get(term) + "\n");

            writer.flush();
            writer.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void run() {
        printParams();
        System.out.println("start " + this.rootPath);
        Integer ntopics = this.ntopics;
        String input = this.filePath;
        this.setModelParams();
        this.mp.T = ntopics;
        this.loadGibbsDataset(input);
        this.convert_dataset();
        this.gibbs_online_init();
        int si;
        double delta; // max L1 change in any single \gamma_ij vector
        for (si = 0; si < this.maxiter; si++) {
            System.out.print("Thread_" + this.rootPath + ":" + si + " ... ");
            if (si > 0 && si % saveStep == 0) {
                storeResult(si);
            }
            delta = this.dsparsetm_infer();
            System.out.println(delta + " ... ");
            if (delta < this.convtol)
                break;
        }
        storeResult(-1);
    }

    public void storeResult(int num) {
        String append = "final";
        if (num > -1) {
            append = num + "";
        }
        this.save_phi(this.ParentFile, "model-" + append);
        this.save_wordmap(this.ParentFile);
        this.save_theta(this.ParentFile, "model-" + append);
        this.save_others(this.ParentFile, "model-" + append);
        this.save_counts(this.ParentFile, "model-" + append);
        this.print_topics(20);
    }

    public void save_counts(String outdir, String model_name) { //m_nkw
        double T = mp.T;
        double W = ds.W;

        BufferedWriter writer = IOUtil.getWriter(outdir + "_" + model_name + ".count", "utf-8");
        try {
            for (int k = 0; k != T; k++) {
                for (int w = 0; w != W; w++) {
                    writer.append(c.m_nkw[k][w] + " ");
                }
                writer.append("\n");
            }
            writer.flush();
            writer.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void save_theta(String outdir, String model_name) {
        double[][] theta = this.est_theta();

        double T = mp.T;
        double D = ds.D;

        BufferedWriter writer = IOUtil.getWriter(outdir + model_name + ".theta", "utf-8");
        try {
            for (int d = 0; d != D; d++) {
                for (int t = 0; t != T; t++) {
                    writer.append(theta[d][t] + " ");
                }
                writer.append("\n");
            }
            writer.flush();
            writer.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void save_others(String parentFile2, String string) {
        // TODO Auto-generated method stub

    }

    public void printParams() {
        System.out.println("ntopics:" + this.ntopics
                + "\tniters:" + this.maxiter
                + "\tsaveStep:" + saveStep
                + "\tParentFile:" + ParentFile
        );
    }

    public static void DsparseTMs(int ntopics, int cpu, String sPath, int niters, int saveStep) throws InterruptedException {

        Thread[] Threadpool = new Thread[cpu];
        for (int i = 0; i < cpu; i++) {

            DsparseTM onedd = new DsparseTM(i + "", sPath, ntopics, niters, saveStep);
            Threadpool[i] = new Thread(onedd);
            Threadpool[i].start();

        }
        for (int i = 0; i < cpu; i++) {
            Threadpool[i].join();
        }

    }
}
